//#include <malloc.h>
#include <kstdio.h>
#include <BuddyAllocator.h>
#include <SlabAllocator.h>

Slab::Slab(uint32 sz){
	objSize = sz;
	addr_t objAddr = (uint32) this;
	objNum = (PGSIZE - sizeof(Slab)) / sz;
	/*TODO: Добавить, что-бы в объект(ы) Frame которые отвечают за фреймы отданные в
	        распоряжение распределителя был занесен адрес объекта Slab отвечающего за
	        выделение участков. Эта информация затем потребуется при освобождении места 
	        в участке.*/
	addr_t page = objAddr + sizeof(Slab) - PGSIZE;
	int i;
	Link *freeObj;
	for(i = 0; i < objNum; i++){
		freeObj = (Link *)(page + sz * i);
		objList.push(freeObj);
	}
}

void * Slab::operator new(size_t sz, uint32 pr){
	
	Frame *frm = zone_normal->_allocFrame(0);
	kprintf("SlabFrm: %X\n", frm);
	if(frm){
		uint32 page = (addr_t) _va(_frm_to_addr(frm));
		void * slab_addr = (void *)(page + PGSIZE - sizeof(Slab));
		frm->setSlab((addr_t)slab_addr);
		return slab_addr;
	}
	
	/*
	uint32 page = (uint32) malloc(PGSIZE);
	if(page){
		//printf("SlabStart: %X, SlabEnd: %X\n", page, (page + PGSIZE));
		return (void *)(page + PGSIZE - sizeof(Slab));
	}
	*/
	kprintf("No more pages!\n");
	return 0;
}

void * Slab::s_malloc(size_t sz){
	if(objCount == objNum) return NULL;
	Link *retAddr = objList.pop();
	objCount++;
	return (void *) retAddr;
}
/*
inline uint32 Slab::isFull(){
	if(objCount == objNum) return 1; 
	else return 0;
}
*/
/*TODO: Добавить проверку диапазона, попадает ли указатель в диапазон адресов охватываемых участком*/
void Slab::s_mfree(void *ptr){
	if(ptr){
		kprintf("Free\n");
		objList.push((Link *)ptr);
		objCount--;
	}else{
		kprintf("Error! Pointer is NULL!\n");
	}
}

/******************* Методы класса SlabAllocator **************************/
/**/
SlabAllocator::SlabAllocator(size_t sz){
	//TODO!!!Проверить параметр sz!
	kprintf("SA_CONSTRUCT!\n");
	objSize = sz;
}
/*
inline Slab * SlabAllocator::allocSlab(){
	if(objSize > 512) return new(1) Slab(objSize);
	return new(2) Slab(objSize);
}
*/
void * SlabAllocator::sa_malloc(){
	void * radr = 0;
	Link *lnk;
	Slab *slb;
	if(notFreeSlabs.getSize()){
		lnk = notFreeSlabs.getFirst();
		slb = get_obj(lnk, Slab, slabs);
		radr = slb->s_malloc(objSize);
		if(slb->isFull()){
			notFreeSlabs.remove(lnk);
			fullSlabs.push(lnk);
		}
	}else if(freeSlabs.getSize()){
		lnk = freeSlabs.pop();
		slb = get_obj(lnk, Slab, slabs);
		radr = slb->s_malloc(objSize);
		notFreeSlabs.push(lnk);
	}else{
		slb = allocSlab();
		radr = slb->s_malloc(objSize);
		notFreeSlabs.push(&slb->slabs);
	}
	return radr;
}

void SlabAllocator::sa_mfree(void *obj){
	Frame *frm = _addr_to_frm(_pa(obj));
	Slab *slb = (Slab *)frm->getSlab();
	if(slb){
		slb->s_mfree(obj);
	}else{
		kprintf("SA_ERROR: SA_MFREE, no Slab!\n");
	}
}

void * SlabAllocator::operator new(size_t sz, void *addr){
	return addr;
}

//Распределитель памяти под сами распределители
SlabAllocator *privateAllocator;
SlabAllocator prvSlbAlloc(sizeof(Slab));

CommonAllocator::CommonAllocator(){
	kprintf("!CACONSTRUCT!\n");
	int i, order = MIN_ORDER;
	for(i = 0; i < COMMON_ALLOCS_NR; ++i, ++order){
		slabAllocs[i].init((size_t) 1UL << order);
	}
}

void * CommonAllocator::ca_malloc(size_t sz){
	uint32 order = MIN_ORDER, size = 1UL << order;
	while(size < sz && order <= MAX_ORDER){
		++order;
		size <<= 1;
	}
	//kprintf("SLAB_ORDER:%i SZ: %i\n", order, sz);
	return slabAllocs[order - MIN_ORDER].sa_malloc();
}

void CommonAllocator::ca_mfree(void *obj){
	Frame *frm = _addr_to_frm(_pa(obj));
	Slab *slb = (Slab *)frm->getSlab();
	if(slb){
		slb->s_mfree(obj);
	}else{
		kprintf("CA_ERROR: CA_MFREE, no Slab!\n");
	}
}

void * CommonAllocator::operator new(size_t sz, void *addr){
	return addr;
}

CommonAllocator *commonAllocator;


void * operator new(size_t sz, SlabAllocator *sa){
	void * addr = sa->sa_malloc();
	if(addr) return addr;
	kprintf("No space!\n");
	return 0;
}

void * operator new(size_t sz, SlabAllocator &sa){
	void * addr = sa.sa_malloc();
	if(addr) return addr;
	kprintf("No space!\n");
	return 0;
}

void * operator new(size_t sz){
	if(sz <= (1UL << MAX_ORDER)){
		void *addr = commonAllocator->ca_malloc(sz);
		if(addr) return addr;
		kprintf("CA_ERROR(operator new): No space!\n");
	}
	return 0;
}

template<class X> void destroy(X* p, SlabAllocator *sa){
	p->~X();
	sa->sa_mfree(p);
}

void operator delete(void *obj){
	kprintf("delete: %X\n", obj);
	commonAllocator->ca_mfree(obj);
}



